const obj = {
  a: 1
}

function foo(x, y) {
  console.log(this.a, x + y);
  return 'hello'
}

// foo.call(obj, 2, 3); // 1 5

const R = foo.bind(obj, 2, 3)
console.log(R());



Function.prototype.mycall = function (context, ...args) {
  if (typeof (this) !== 'function') {
    throw new TypeError('this must be a function');
  }
  context = context || window;

  const key = Symbol('fn')
  context[key] = this
  let res = context[key](...args);
  delete context[key]
  return res
}

Function.prototype.myapply = function (context, args) {
  if (typeof (this) !== 'function') {
    throw new TypeError('this must be a function');
  }
  context = context || window;

  const key = Symbol('fn')
  context[key] = this
  let res = context[key](...args);
  delete context[key]
  return res
}

Function.prototype.mybind = function (context, ...args) {
  if (typeof (this) !== 'function') {
    throw new TypeError('this must be a function');
  }
  context = context || window
  const self = this
  return function fn(...arg) {
    if (this.__proto__ === fn.prototype) {       // 如果是被 new 调用，返回 foo 实例对象
      return new self(...args, ...arg)
    } else {
      return self.apply(this, [...args, ...arg])
    }
  }
}

// const res = foo.mycall(obj, 2, 3); // 1 5
const res = foo.myapply(obj, [2, 3])
console.log(res); // hello